<?php

defined('SYSPATH') or die('Access error!');
/**
 * session 类
 */
abstract class Session
{

    public static $default = 'native';
    public static $instances = array();

    /**
     *  初始化session
     * 
     * @param type $type
     * @param type $id
     * @return type 
     */
    public static function instance($type = NULL, $id = NULL)
    {
        if ($type === NULL)
        {
            $type = Session::$default;
        }

        if (!isset(Session::$instances[$type]))
        {
            $config = Core::config('session');
            $class = 'Session_' . ucfirst($type);
            Session::$instances[$type] = $session = new $class($config, $id);
            register_shutdown_function(array($session, 'write'));
        }
        return Session::$instances[$type];
    }

    protected $_name = 'session';
    protected $_lifetime = 0;
    protected $_encrypted = FALSE;
    protected $_data = array();
    protected $_destroyed = FALSE;

    public function __construct(array $config = NULL, $id = NULL)
    {
        if (isset($config['name']))
        {
            $this->_name = (string) $config['name'];
        }

        if (isset($config['lifetime']))
        {
            $this->_lifetime = (int) $config['lifetime'];
        }

        if (isset($config['encrypted']))
        {
            if ($config['encrypted'] === TRUE)
            {
                $config['encrypted'] = 'default';
            }
            $this->_encrypted = $config['encrypted'];
        }
        $this->read($id);
    }

    /**
     *   输出session 自动调用
     * 
     *     echo $session;
     * @return  string
     * @uses    Encrypt::encode
     */
    public function __toString()
    {
        $data = serialize($this->_data);

        if ($this->_encrypted)
        {
            $data = Encrypt::instance($this->_encrypted)->encode($data);
        }
        else
        {
            $data = base64_encode($data);
        }

        return $data;
    }

    /**
     *   返回当前session的数据
     *     $data = $session->as_array();
     * @return  array
     */
    public function & as_array()
    {
        return $this->_data;
    }

    /**
     *   返回session id
     *     $id = $session->id();
     * @return  string
     * @since   3.0.8
     */
    public function id()
    {
        return NULL;
    }

    /**
     * 返回session的name
     *
     *     $name = $session->name();
     *
     * @return  string
     * @since   3.0.8
     */
    public function name()
    {
        return $this->_name;
    }

    /**
     * 获取某个session数据
     *   例如：
     *     $foo = $session->get('user');
     *
     * @param   string   
     * @param   mixed    
     * @return  mixed
     */
    public function get($key, $default = NULL)
    {
        return array_key_exists($key, $this->_data) ? $this->_data[$key] : $default;
    }

    public function get_detail($key, $default = 'id')
    {
        $detail = $this->get($key);
        return array_key_exists($default, $detail) ? $detail[$default] : '';
    }

    /**
     * 只获取一次某个session一次后就删除掉
     *
     *     $bar = $session->get_once('bar');
     *
     * @param   string  variable name
     * @param   mixed   default value to return
     * @return  mixed
     */
    public function get_once($key, $default = NULL)
    {
        $value = $this->get($key, $default);

        unset($this->_data[$key]);

        return $value;
    }

    /**
     * 设置session
     *
     *     $session->set('foo', 'bar');
     *
     * @param   string   variable name
     * @param   mixed    value
     * @return  $this
     */
    public function set($key, $value)
    {
        $this->_data[$key] = $value;

        return $this;
    }

    public function set_detail($key, $default = 'id', $value = '')
    {
        $detail = $this->get($key);
        if (array_key_exists($default, $detail))
        {
            $detail[$default] = $value;
        }
        $this->set($key, $detail);
    }

    /**
     * 设置session
     *
     *     $session->bind('foo', $foo);
     *
     * @param   string  
     * @param   mixed  
     * @return  $this
     */
    public function bind($key, & $value)
    {
        $this->_data[$key] = & $value;

        return $this;
    }

    /**
     * 删除某个session
     *     $session->delete('foo');
     * @param   string 
     * @param   ...
     * @return  $this
     */
    public function delete($key)
    {
        $args = func_get_args();

        foreach ($args as $key)
        {
            unset($this->_data[$key]);
        }

        return $this;
    }

    /**
     * Loads existing session data.
     *
     *     $session->read();
     *
     * @param   string   session id
     * @return  void
     */
    public function read($id = NULL)
    {
        if (is_string($data = $this->_read($id)))
        {
            try
            {
                if ($this->_encrypted)
                {
                    // Decrypt the data using the default key
                    $data = Encrypt::instance($this->_encrypted)->decode($data);
                }
                else
                {
                    // Decode the base64 encoded data
                    $data = base64_decode($data);
                }

                // Unserialize the data
                $data = unserialize($data);
            } catch (Exception $e)
            {
                // Ignore all reading errors
            }
        }

        if (is_array($data))
        {
            // Load the data locally
            $this->_data = $data;
        }
    }

    /**
     * 重启某个sessionp
     *
     *     $id = $session->regenerate();
     *
     * @return  string
     */
    public function regenerate()
    {
        return $this->_regenerate();
    }

    /**
     * Sets the last_active timestamp and saves the session.
     *
     *     $session->write();
     *
     * [!!] Any errors that occur during session writing will be logged,
     * but not displayed, because sessions are written after output has
     * been sent.
     *
     * @return  boolean
     * @uses    Core::$log
     */
    public function write()
    {
        if (headers_sent() OR $this->_destroyed)
        {
            // Session cannot be written when the headers are sent or when
            // the session has been destroyed
            return FALSE;
        }

        // Set the last active timestamp
        $this->_data['last_active'] = time();

        try
        {
            return $this->_write();
        } catch (Exception $e)
        {
            // Log & ignore all errors when a write fails
            Core::$log->add(Core::ERROR, Core::exception_text($e))->write();

            return FALSE;
        }
    }

    /**
     * 销毁所有session
     *
     *     $success = $session->destroy();
     *
     * @return  boolean
     */
    public function destroy()
    {
        if ($this->_destroyed === FALSE)
        {
            if ($this->_destroyed = $this->_destroy())
            {
                // The session has been destroyed, clear all data
                $this->_data = array();
            }
        }

        return $this->_destroyed;
    }

    /**
     * Loads the raw session data string and returns it.
     *
     * @param   string   session id
     * @return  string
     */
    abstract protected function _read($id = NULL);

    /**
     * Generate a new session id and return it.
     *
     * @return  string
     */
    abstract protected function _regenerate();

    /**
     * Writes the current session.
     *
     * @return  boolean
     */
    abstract protected function _write();

    /**
     * Destroys the current session.
     *
     * @return  boolean
     */
    abstract protected function _destroy();
}
